package org.infodavid.common.services;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.DateTime;

import org.infodavid.common.reflect.InstantiationHelper;
import org.infodavid.common.scheduler.impl.SchedulerServiceImpl;
import org.infodavid.common.util.PropertiesUtil;

/**
 * The Class SchedulerService.
 */
public abstract class SchedulerService extends SchedulerServiceImpl {

	/** The singleton. */
	private static final SchedulerService SINGLETON;

	/** The Constant HOUR_SYSTEM_PROPERTY. */
	public static final String HOUR_SYSTEM_PROPERTY = SchedulerService.class
			.getPackage().getName() + ".hour";

	/** The Constant MINUTE_SYSTEM_PROPERTY. */
	public static final String MINUTE_SYSTEM_PROPERTY = SchedulerService.class
			.getPackage().getName() + ".minute";

	/** The Constant ENABLED_SYSTEM_PROPERTY. */
	public static final String ENABLED_SYSTEM_PROPERTY = SchedulerService.class
			.getPackage().getName() + ".enabled";

	/** The Constant DEFAULT_HOUR. */
	public static final byte DEFAULT_HOUR = 23;

	/** The Constant DEFAULT_MINUTE. */
	public static final byte DEFAULT_MINUTE = 0;

	/** The Constant log. */
	private static final Log LOGGER = LogFactory.getLog(SchedulerService.class);

	static {
		try {
			SINGLETON = InstantiationHelper.getInstance().newInstance(
					SchedulerService.class);
		} catch (final Exception e) {
			LOGGER.fatal(
					"Can not instantiate the "
							+ SchedulerService.class.getName(), e);

			throw new Error(e);
		}
	}

	/**
	 * returns the singleton.
	 *
	 * @return
	 */
	public static SchedulerService getInstance() {
		return SINGLETON;
	}

	/**
	 * Instantiates a new service.
	 *
	 * @throws Exception
	 */
	protected SchedulerService(final String configFilename,
			final String dataSourceJndiName) throws Exception {
		super(configFilename, dataSourceJndiName);
	}

	/**
	 * Gets the daily time.
	 *
	 * @return the daily time (hour and minute)
	 */
	private byte[] getDailyTime() {
		String value = PropertiesUtil.getInstance().getSystemPropertyValue(
				HOUR_SYSTEM_PROPERTY, null);
		final byte[] results = new byte[2];

		try {
			results[0] = value == null ? DEFAULT_HOUR : Byte.parseByte(value);

			if (results[0] < 0 || results[0] > 23) {
				LOGGER.warn("Invalid hour in the daily time definition, check system property: "
						+ value);

				results[0] = DEFAULT_HOUR;
			}
		} catch (final Exception e) {
			LOGGER.warn(
					"Invalid hour in the daily time definition, check system property",
					e);

			results[0] = DEFAULT_HOUR;
		}

		value = PropertiesUtil.getInstance().getSystemPropertyValue(
				MINUTE_SYSTEM_PROPERTY, null);

		try {
			results[1] = value == null ? DEFAULT_MINUTE : Byte.parseByte(value);

			if (results[0] < 0 || results[0] > 59) {
				LOGGER.warn("Invalid minute in the daily time definition, check system property: "
						+ value);

				results[1] = DEFAULT_MINUTE;
			}
		} catch (final Exception e) {
			LOGGER.warn(
					"Invalid minute in the daily time definition, check system property",
					e);

			results[1] = DEFAULT_MINUTE;
		}

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Daily time is " + results[0] + ":" + results[1]);
		}

		return results;
	}

	/**
	 * Compute daily date.
	 *
	 * @return the date time
	 */
	public DateTime getNextDailyScheduleTime() {
		final byte[] dailyTime = getDailyTime();
		final DateTime now = new DateTime();
		DateTime result;
		
		//reset second and millisecond to 0
		result = now.withHourOfDay(dailyTime[0]).withMinuteOfHour(dailyTime[1]).withSecondOfMinute(0).withMillisOfSecond(0);		
		
		if (!result.isAfterNow()) {
			// tomorrow
			result = result.plusDays(1);
		}

		return result;
	}
}
